/*
** Copyright (c) 2008 Mobile GCalendarManager
**
** Permission is hereby granted, free of charge, to any person
** obtaining a copy of this software and associated documentation
** files (the "Software"), to deal in the Software without
** restriction, including without limitation the rights to use,
** copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the
** Software is furnished to do so, subject to the following
** conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
** OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
** HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
** OTHER DEALINGS IN THE SOFTWARE.
*/

/**
 *
 */
package au.edu.mq.comp.itec800.mgc.ui.cli;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import au.edu.mq.comp.itec800.mgc.MGC;
import au.edu.mq.comp.itec800.mgc.lib.GCalendar;
import au.edu.mq.comp.itec800.mgc.lib.calendar.GEvent;
import au.edu.mq.comp.itec800.mgc.ui.cli.commands.CalendarCommand;
import au.edu.mq.comp.itec800.mgc.ui.cli.commands.CommandLoader;
import au.edu.mq.comp.itec800.mgc.ui.cli.commands.CommentCommand;
import au.edu.mq.comp.itec800.mgc.ui.cli.commands.EventCommand;
import au.edu.mq.comp.itec800.mgc.ui.cli.commands.GuestCommand;
import au.edu.mq.comp.itec800.mgc.ui.cli.commands.MainCommand;
import au.edu.mq.comp.itec800.mgc.ui.cli.commands.NotificationCommand;
import au.edu.mq.comp.itec800.mgc.util.CommandUtils;
import au.edu.mq.comp.itec800.mgc.util.MGCUtils;
import au.edu.mq.comp.itec800.mgc.util.Utils;

import com.google.gdata.util.ServiceException;

/**
 * Implements an enumerated type for the Mobile GCalendar's inner-contexts'
 * management. Each enumerated value represents a context, which can be
 * switched between one another. Each context is associated to a given
 * CommandLauncher, which implements the logic of each command associated to
 * each context.
 *
 *
 * @see ICommand
 * @see IContext
 *
 * @author Laurent Malvert [laurent.malvert@students.mq.edu.au]
 * @author Kenfe-Mickael Laventure [kenfe-mickael.laventure@students.mq.edu.au]
 */
public enum Context implements IContext, ICommand
{
  // context     parent     command-launcher
  main          (           MainCommand.class)
  {
    @Override
    public boolean  execute(String ... args)
    {
      if (MGCUtils.getGAccount() != null)
      {
        MGCUtils.getGAccount().getCalendarManager().setCurrentCalendar(null);
        MGC.getInstance().setContext(this);
      }
      return (true);
    }
  },
  calendar      (main,      CalendarCommand.class)
  {
    @Override
    public boolean  execute(String ... args)
    {
      int             calId = -1;
      GCalendar       cal   = null;
      List<GCalendar> cals  = null;

      //if no argument, don't switch the context
      if (args.length == 0)
      {
        //if a calendar is already set, this mean that we were in a child
        if (MGCUtils.getGAccount().getCalendarManager().getCurrentCalendar() != null)
          MGC.getInstance().setContext(this);
        return (true);
      }
      //if the argument directly a number ?
      try
      {
        cals  = MGCUtils.getGAccount().getCalendarManager().getCombined();
        calId = Integer.parseInt(args[0]);
        cal   = cals.get(calId);
      }
      catch (final IOException e)
      {
        logger.error(e.getMessage(), e);
      }
      catch (final ServiceException e)
      {
        logger.error(e.getMessage(), e);
      }
      catch (final NumberFormatException ex)
      {
        for (final GCalendar e : cals)
        {
          if (e.getName().compareToIgnoreCase(args[0]) == 0)
          {
            cal = e;
            break;
          }
        }
      }
      catch (final IndexOutOfBoundsException e)
      {
        logger.warn(e.getMessage(), e);
      }
      finally
      {
        if (cal != null)
        {
          MGCUtils.getGAccount().getCalendarManager().setCurrentCalendar(cal);
          MGC.getInstance().setContext(this);
        }
      }
      return (true);
    }
  },
  event         (calendar,  EventCommand.class)
  {
    @Override
    public boolean execute(String ... args)
    {
      if (args.length > 0)
      {
        try
        {
          int           idx;
          List<GEvent>  evts  = MGCUtils.getGAccount().getCalendarManager()
                                .getEventManager().getAll();

          idx = Integer.parseInt(args[0]);
          if (Utils.isInBound(evts, idx))
          {
            MGCUtils.getGAccount().getCalendarManager().getEventManager()
            .setCurrentEvent(evts.get(idx));
            MGC.getInstance().setContext(this);
          }
        }
        catch (IOException e)
        {
          logger.debug(e.getMessage(), e);
        }
        catch (ServiceException e)
        {
          logger.debug(e.getMessage(), e);
        }
        catch (final NumberFormatException e)
        {

        }
        catch (final IndexOutOfBoundsException e)
        {

        }
      }
      else
      {
        GEvent  evt = MGCUtils.getGAccount().getCalendarManager()
                      .getEventManager().getCurrentEvent();
        if (evt != null)
        {
          MGC.getInstance().setContext(this);
        }
      }
      return (true);
    }
  },
  notification  (event,     NotificationCommand.class),
  guest         (event,     GuestCommand.class),
  comment       (event,     CommentCommand.class),

  /*
   * short forms / aliases
   */
  m (main),
  C (calendar),
  n (notification),
  g (guest),
  c (comment);



  private static final Logger       logger  = Logger.getLogger(Context.class);

  private Context                   parent          = null;
  private Class<? extends ICommand> commanderClass  = null;
  private String                    hlp       = "";
  private ICommand                  aliases[] = null;
  private ICommand                  master    = null;


  private Context()
  {
    this((String)null, (ICommand)null);
  }

  private Context(final String hlp)
  {
    this(hlp, null);
  }

  private Context(final ICommand master)
  {
    this((String)null, master);
  }

  private Context(final String hlp, final ICommand master)
  {
    this.master = ((master != null) && (master.name().equals(name()) == false)) ? master : null;
    this.hlp    = (hlp != null) ? hlp : ((this.master != null) ? this.master.help() : "");
  }


  /**
   * Private default constructor for Context enumerated types.
   *
   * @param parent
   *                The parent context, or null if there is none.
   * @param commanderClass
   *                The associated commanderClass.
   */
  private Context(final Context parent,
                  final Class<? extends ICommand> commanderClass)
  {
    this.parent         = parent;
    this.commanderClass = commanderClass;
  }

  /**
   * Private default constructor for root Context enumerated types.
   *
   * @param commanderClass
   *                The associated commanderClass.
   */
  private Context(final Class<? extends ICommand> commanderClass)
  {
    this(null, commanderClass);
  }

  /*
   * (non-Javadoc)
   * @see au.edu.mq.comp.itec800.mgc.ui.cli.IContext#getParent()
   */
  public IContext                   getParent()
  {
    return (parent);
  }

  /*
   * (non-Javadoc)
   * @see au.edu.mq.comp.itec800.mgc.ui.cli.IContext#getName()
   */
  public String                     getName()
  {
    return (name());
  }

  /**
   * Returns the corresponding commander-class for the current context.
   *
   * @return
   *                The "commander" class corresponding to the current
   *                context.
   */
  public Class<? extends ICommand>  getCommanderClass()
  {
    return (commanderClass);
  }

  /*
   * (non-Javadoc)
   * @see au.edu.mq.comp.itec800.mgc.ui.cli.IContext#isRoot()
   */
  public boolean                    isRoot()
  {
    return (getParent() == null);
  }

  /**
   * Returns the corresponding ICommand implementation for a given command
   * identification string.
   *
   *
   * @param command
   *                The command used to identify the appropriate
   *                command launcher.
   *
   * @return
   *                The corresponding ICommand implementation
   *                (thus the corresponding "commander").
   */
  public  ICommand                  call(final String command)
  {
    ICommand                        cmd;

    if (isAlias())
    {
      cmd = ((IContext) getTopMaster()).call(command);
    }
    else
    {
      Context                   parent          = (Context) getParent();
      Class<? extends ICommand> commanderClass  = getCommanderClass();

      do
      {
        cmd             = CommandLoader.load(commanderClass, command);
        if ((parent == null))
        {
          break;
        }
        commanderClass  = parent.getCommanderClass();
        parent          = (Context) parent.getParent();
      } while (cmd == null);
      //is this the name of a context? if yes switch to it
      if (cmd == null)
      {
        try
        {
        	cmd = Context.valueOf(Context.class, command);
        	//if the corresponding context is not a direct sibling, ignore the cmd
        	//if not the real switching will occur in the execute method after
        	//argument parsing (if needed)
        	cmd = cmd.getTopMaster();
        	if ((this != ((Context)cmd).getParent()) &&
        	    (getParent() != ((Context)cmd)) && (this != cmd))
        	{
            cmd = null;
        	}
        }
        catch (IllegalArgumentException e)
        {
          /* silently ignore */
        }
      }
      cmd = (cmd != null ? cmd : this);
    }
    return (cmd);
  }

  public ICommand                   getTopMaster()
  {
    return (CommandUtils.getTopMaster(this));
  }

  /* (non-Javadoc)
   * @see au.edu.mq.comp.itec800.mgc.ui.cli.IContext#getTag()
   */
  public String                     getTag()
  {
    // TODO: return the context's tag for the UI.
    return (null);
  }

  /* (non-Javadoc)
   * @see au.edu.mq.comp.itec800.mgc.ui.cli.ICommand#execute(java.lang.String[])
   *
   * just set the new context
   *  (effectively a noop if the context didn't change)
   */
  public boolean                    execute(final String ... args)
  {
    MGC.getInstance().setContext(this);
    return (isAlias() ? getMaster().execute(args) : false);
  }

  public boolean                    needsAuthentication()
  {
    return (false);
  }

  public ICommand                   getMaster()
  {
    return (master);
  }

  public boolean                    isAlias()
  {
    return (CommandUtils.isAlias(this));
  }

  public ICommand[]       getAliases()
  {
    if (aliases == null)
    {
      final List<ICommand>  alist =
        new ArrayList<ICommand>(Context.values().length);

      for (final Context cmd : Context.values())
      {
        if (cmd.isAlias() && cmd.getMaster().name().equals(name()))
        {
          alist.add(cmd);
        }
      }
      alist.toArray(aliases = new Context[alist.size()]);
    }
    return (aliases);
  }

  public String                     help()
  {
    return ("");
  }
}
